package pkg.nudroid.translator;

%%

%byaccj
%line
%column

%{
  /* Store a reference to the parser object */
  private Parser yyparser;

  /* Constructor taking an additional parser object */
  public Yylex(java.io.Reader r, Parser yyparser) {
    this(r);
    this.yyparser = yyparser;
  }
  
  /* Basic print match statement */
  public void print(String str) {
	System.out.println("Matched: " + str);
  }
  
  public void print(String msg, String str) {
	System.out.println("-- " + msg + " " + str);
  }
  
  /* Print error; desc: description of the error, err: error msg */
  /* We can also do in this way: throw new Error(desc, err); */
  public void error(String desc, String err) {
  	String msg = desc;
  	msg += " at (l " +yyline+ ", c "+yycolumn+"): ";
  	msg += err;
	System.out.println(msg);
  }

  /* Removes white space from string */
  public String strip_ws(String yytext) {
	return yytext.replaceAll("[ \t\n\f\r]","");
  }
  
  /* Trims the attribute values */
  public String trim_attr_value(String attr_value) {
  	String str = strip_quotes(attr_value);
  	
  	/* Take out leading and trailing white space */
  	//if (str.substring(0,1).equals(" "))
  	//	str = str.substring(1,str.length());
  	//if (str.substring(str.length()-1,str.length()).equals(" "))
  	//	str = str.substring(0,str.length()-1);
  	
  	return str.trim();
  }
  
  /* Trim the attribute values that are identifiers, etc */
  public String trim_id_value(String attr_value) {
  	return strip_ws(strip_quotes(attr_value));
  }
  
  /* Trims the attribute name */ 
  public String trim_attr_name(String attr_name) {
    String str = strip_ws(attr_name);
  	return str.substring(0,str.length()-1);
  }
  
  /* Removes double quotes from strings */
  public String strip_quotes(String yytext) {
  	return yytext.replaceAll("\"","");
  }
  
  /* Returns token and prints */
  public int token (int token) {
  	System.out.println("Send token: " + token);
  	return token;
  }

%}

/* whitespace */
WS = [ \t\n\f\r]*
WR = [ \t\n\f\r]+
S = [ ]*

/* comments */
COM = "<!--" ~"-->"

/* basics */
LETTER = [a-zA-Z]
DIGIT = [0-9]

/* attribute names */
NAME = ({LETTER}[a-zA-Z-]*{LETTER})
ID_NAME = "id" | "pageID" | "styleID" | "functionID" | "textID" | "inputID"
VAL_NAME = "value" | "cond" | "start" | "end"
VAR_NAME = "varID"
APP_NAME = "name"

/* expressions & operators */
OP = "+" | "-" | "/" | "*" | "<" | ">" | "=" | "!" | "(" | ")" | "==" | "<=" | ">=" | "!=" | "&&" | "||"
NUMBER = ({DIGIT}+)

/* identifier values */
ID = ({LETTER}[a-zA-Z_0-9-]*)
VAR = \${ID}

/* attribute and literal values*/
LIT = (\"[^\"]*\")
NUM_LIT = {NUMBER} | (\"{S}{NUMBER}{S}\")
NAME_LIT = {ID} | (\"{S}{ID}{S}\")
VAR_LIT = {VAR} | (\"{S}{VAR}{S}\")

/* text (content between open & close tags) */
TEXT = ([^\<\>]*)

/* self-contained tags 
TAG = (\<\/?[^\>]+\/?\>) */
OPENTAG = (\<[ ]*[^ ])
CT = \>
CT_SELF = "/>"

/* reserved keywords */
T_NUDROID	= "<NuDroid"
T_NAVIGATION = "<navigation"
T_MENU = "<menu"
T_PAGE = "<page"
T_TEXT = "<text"
T_BUTTON = "<button"
T_LINK = "<link"
T_LIST = "<list"
T_IMAGE = "<image"
T_TABLE = "<table"
T_AUDIO = "<audio"
T_VIDEO = "<video"
T_FUNCTION = "<function"
T_VAR = "<var"
T_INPUT = "<input"
T_GETINPUT = "<getInput"
T_GOTO = "<goto"
T_SETVAR = "<setVar"
T_GETVAR = "<getVar"
T_SETTEXT = "<setText"
T_GETTEXT = "<getText"
T_TEST = "<test"
T_TRUE = "<true"
T_FALSE = "<false"
T_LOOP = "<loop"
CT_NUDROID = "</NuDroid>"
CT_NAVIGATION = "</navigation>"
CT_MENU = "</menu>"
CT_PAGE = "</page>"
CT_TEXT = "</text>"
CT_BUTTON = "</button>"
CT_LINK = "</link>"
CT_LIST = "</list>"
CT_IMAGE = "</image>"
CT_TABLE = "</table>"
CT_AUDIO = "</audio>"
CT_VIDEO = "</video>"
CT_FUNCTION = "</function>"
CT_VAR = "</var>"
CT_INPUT = "</input>"
CT_TEST = "</test>"
CT_TRUE = "</true>"
CT_FALSE = "</false>"
CT_LOOP = "</loop>"
CT_SETTEXT = "</setText>"

%state TAG 
/* state that accepts any valid tag, except NuDroid */
%state ATTR			
/* state that accepts a form of attribute name */
%state ATTRVALUE	
/* state that accepts a form of value for attribute */
%state IDVALUE		
/* state that accepts only identifiers */
%state VARVALUE
/* state that accepts only variables */
%state NAMEVALUE		
/* state that accepts only name of application */
%state BODY		
/* state that accepts content between open and close tag */

%state EXPBEGIN
/* state that begins to accept expressions */
%state EXPVALUE		
/* state that accepts expressions */

%state END	
/* state that does not accept anything because the app is done */

%%

{WS}	{ print("Stripped whitespace","");  /* strip out */ }

{COM}	{ print("Stripped comment", yytext());  /* strip out */ }

/******* looking for <NuDroid to begin document */
<YYINITIAL> {T_NUDROID}{WR}				{	yybegin(ATTR); 
											print(yytext()); 
											return token(Parser.T_NUDROID); }

/* if anything else, then return error */
<YYINITIAL, TAG> {OPENTAG}{WS}			{	error("Illegal initial tag", yytext()); }

/******* looking for any valid start tag */
<TAG,BODY> {

 {T_NAVIGATION}{WS}					{	yybegin(ATTR); 
										print(yytext());
										return token(Parser.T_NAVIGATION); }
										
 {T_MENU}{WR}						{	yybegin(ATTR); 
										print(yytext());
										return token(Parser.T_MENU); }
										 
 {T_PAGE}{WR}						{	yybegin(ATTR); 
 										print(yytext());
										return token(Parser.T_PAGE); }

 {T_TEXT}{WR}						{	yybegin(ATTR);									
										print(yytext());
										return token(Parser.T_TEXT); }

 {T_BUTTON}{WR}						{	yybegin(ATTR);								
										print(yytext());
										return token(Parser.T_BUTTON); }

 {T_LINK}{WR}						{	yybegin(ATTR);								
										print(yytext());
										return token(Parser.T_LINK); }
 
 {T_LIST}{WR}						{	yybegin(ATTR); 								
										print(yytext());
										return token(Parser.T_LIST); } 
 
 {T_IMAGE}{WR}						{	yybegin(ATTR);
										print(yytext());
										return token(Parser.T_IMAGE); } 
 
 {T_TABLE}{WR}						{	yybegin(ATTR);								
										print(yytext());
										return token(Parser.T_TABLE); } 
 
 {T_AUDIO}{WR}						{	yybegin(ATTR);								
										print(yytext());
										return token(Parser.T_AUDIO); } 
 
 {T_VIDEO}{WR}						{	yybegin(ATTR);
										print(yytext());
										return token(Parser.T_VIDEO); } 
 
 {T_FUNCTION}{WR}					{	yybegin(ATTR);
										print(yytext());
										return token(Parser.T_FUNCTION); } 
 
 {T_VAR}{WR}						{	yybegin(ATTR);								
										print(yytext());
										return token(Parser.T_VAR); } 
 
 {T_INPUT}{WR}						{	yybegin(ATTR);								
										print(yytext());
										return token(Parser.T_INPUT); } 
 
 {T_GETINPUT}{WR}					{	yybegin(ATTR);
										print(yytext());
										return token(Parser.T_GETINPUT); } 
 
 {T_GOTO}{WR}						{	yybegin(ATTR);								
										print(yytext());
										return token(Parser.T_GOTO); } 
 
 {T_SETVAR}{WR}						{	yybegin(ATTR);
										print(yytext());
										return token(Parser.T_SETVAR); } 
 
 {T_GETVAR}{WR}						{	yybegin(ATTR);
										print(yytext());
										return token(Parser.T_GETVAR); } 
 
 {T_SETTEXT}{WR}					{	yybegin(ATTR);								
										print(yytext());
										return token(Parser.T_SETTEXT); } 
 
 {T_GETTEXT}{WR}					{	yybegin(ATTR);								
										print(yytext());
										return token(Parser.T_GETTEXT); } 
 
 {T_TEST}{WR}						{	yybegin(ATTR);								
										print(yytext());
										return token(Parser.T_TEST); } 
 
 {T_TRUE}{WS}						{	yybegin(ATTR);
										print(yytext());
										return token(Parser.T_TRUE); } 
 
 {T_FALSE}{WS}						{	yybegin(ATTR);
										print(yytext());
										return token(Parser.T_FALSE); } 
 
 {T_LOOP}{WR}						{	yybegin(ATTR);								
										print(yytext());
										return token(Parser.T_LOOP); } 
										
}

/******* looking for attribute name */
<ATTR> {

 {APP_NAME}{WS}={WS}		{	yybegin(NAMEVALUE);
 								print(yytext());
 								
 								String str = trim_attr_name((yytext()));
 								
 								return token(Parser.P_NAME); }

 {ID_NAME}{WS}={WS}			{	yybegin(IDVALUE);
								print(yytext());
								
								String str = trim_attr_name((yytext()));
								
								if (str.equals("id"))
									return token(Parser.P_ID);
								if (str.equals("pageID"))
									return token(Parser.P_PAGEID);
								if (str.equals("functionID"))
									return token(Parser.P_FUNCTIONID);
								if (str.equals("textID"))
									return token(Parser.P_TEXTID);
								if (str.equals("inputID"))
									return token(Parser.P_INPUTID);	}

 {VAL_NAME}{WS}={WS}		{	yybegin(EXPBEGIN);
								print(yytext());
								
								String str = trim_attr_name((yytext()));
								
								if (str.equals("value"))
									return token(Parser.P_VALUE);
								if (str.equals("cond"))
									return token(Parser.P_CONDITION); 
								if (str.equals("start"))
									return token(Parser.P_START);
								if (str.equals("end"))
									return token(Parser.P_END); }
							
 {VAR_NAME}{WS}={WS}		{	yybegin(VARVALUE);
								print(yytext());
								
								String str = trim_attr_name((yytext()));
								
								return token(Parser.P_VARID); }
 
 								
 {NAME}{WS}={WS}			{	yybegin(ATTRVALUE);
								print(yytext());
								
								String str = trim_attr_name((yytext()));
								
								if (str.equals("url"))
									return token(Parser.P_URL);
								if (str.equals("email"))
									return token(Parser.P_EMAIL);
								if (str.equals("bullet-style"))
									return token(Parser.P_BULLET_STYLE);
								if (str.equals("type"))
									return token(Parser.P_TYPE);
								if (str.equals("cols"))
									return token(Parser.P_COLS);
								if (str.equals("type"))
									return token(Parser.P_TYPE);
								if (str.equals("label"))
									return token(Parser.P_LABEL);
								if (str.equals("position"))
									return token(Parser.P_POSITION);
								if (str.equals("height"))
									return token(Parser.P_HEIGHT);
								if (str.equals("width"))
									return token(Parser.P_WIDTH);
								if (str.equals("location"))
									return token(Parser.P_LOCATION);
								if (str.equals("size"))
									return token(Parser.P_SIZE);
								if (str.equals("color"))
									return token(Parser.P_COLOR);
								if (str.equals("font"))
									return token(Parser.P_FONT);
								if (str.equals("lorientation"))
									return token(Parser.P_LORIENTATION); }

 {CT}{WS}						{	yybegin(BODY);
									print(yytext());
									return token(yycharat(0)); }
								
 {CT_SELF}{WS}					{	yybegin(BODY);
 									print(yytext());
 									return token(Parser.CT_SELF_CLOSING); }		
 								
}

/******* looking for attribute value */
<ATTRVALUE> {

 {NUM_LIT}{WS}		{	yybegin(ATTR);
 						print(yytext());
 						
 						yyparser.yylval = new ParserVal(Double.parseDouble(trim_attr_value(yytext())));
 						return token(Parser.NUMBER);  }

 {LIT}{WS}			{	yybegin(ATTR);
 						print(yytext());
 						
 						yyparser.yylval = new ParserVal(trim_attr_value(yytext()));
 						return token(Parser.STRING); }

}

<IDVALUE> {

 {NAME_LIT}{WS}				{	yybegin(ATTR);
								print(yytext());
							
								yyparser.yylval = new ParserVal(trim_id_value(yytext()));
								return token(Parser.IDENTIFIER); }

 {VAR_LIT}{WS}				{	yybegin(ATTR);
								print(yytext());
 						
 								yyparser.yylval = new ParserVal(trim_id_value(yytext()));
								return token(Parser.IDENTIFIER); }
								
 "?."?{WS}					{	error("Invalid id", yytext()); }
}

<NAMEVALUE> {

 {NAME_LIT}{WS}				{	yybegin(ATTR);
								print(yytext());
							
								yyparser.yylval = new ParserVal(trim_id_value(yytext()));
								return token(Parser.IDENTIFIER); }	

 "?."?{WS}					{	error("Invalid application name", yytext()); }
 
}

<VARVALUE> {

 {VAR_LIT}{WS}				{	yybegin(ATTR);
								print(yytext());
								
								yyparser.yylval = new ParserVal(trim_id_value(yytext()));
								return token(Parser.IDENTIFIER); }
								
 "?."?{WS}					{	error("Invalid variable", yytext()); }
}

<EXPBEGIN> \"				{	yybegin(EXPVALUE);
								print(yytext()); }

<EXPVALUE> {

 {NUMBER}{WS}				{	print(yytext());
 								yyparser.yylval = new ParserVal(Double.parseDouble(yytext()));
	 							return token(Parser.NUMBER);	}
	 							
 {OP}{WS}					{	print(yytext());
 								
 								String str = strip_ws(yytext());
 								
 								if (str.equals("=="))
 									return token(Parser.RELOP_EQ);
 								if (str.equals(">="))
 									return token(Parser.RELOP_GEQ);
 								if (str.equals("<="))
 									return token(Parser.RELOP_LEQ);
 								if (str.equals("!="))
 									return token(Parser.RELOP_NEQ);
 								if (str.equals("||"))
 									return token(Parser.LOGIC_OR);
 								if (str.equals("&&"))
 									return token(Parser.LOGIC_AND);
 								else
 									return token(yycharat(0));	}
	 							
 {VAR}{WS}					{	print(yytext());
								yyparser.yylval = new ParserVal(strip_ws(yytext()));
 								return token(Parser.IDENTIFIER); }

 \"{WS}						{	yybegin(ATTR);
 								print(yytext()); }
}

/******* looking for text or content */
<BODY> {TEXT}				{	print(yytext());
								yyparser.yylval = new ParserVal(yytext());
								return token(Parser.UNFORMATTED_STRING); }

/******* looking for closing tag */
<TAG, BODY> {

 {CT_NUDROID}{WS}			{	yybegin(END);
 								print(yytext());
 								return token(Parser.CT_NUDROID); }

 {CT_NAVIGATION}{WS}		{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_NAVIGATION); }

 {CT_MENU}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_MENU); }
 							
 {CT_PAGE}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_PAGE); }

 {CT_TEXT}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_TEXT); }
 
 {CT_BUTTON}{WS}			{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_BUTTON); }
  
 {CT_LINK}{WS}				{	yybegin(BODY);
 								print(yytext());
 								return token(Parser.CT_LINK); }
  
 {CT_LIST}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_LIST); }
  
 {CT_IMAGE}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_IMAGE); }
  
 {CT_TABLE}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_TABLE); }
  
 {CT_AUDIO}{WS} 			{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_AUDIO); }
 
 {CT_VIDEO}{WS} 			{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_VIDEO); }
 
 {CT_FUNCTION}{WS}			{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_FUNCTION); }
 							
 {CT_VAR}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_VAR); }
 
 {CT_INPUT}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_INPUT); }
 
 {CT_TEST}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_TEST); }
 
 {CT_TRUE}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_TRUE); }
 
 {CT_FALSE}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_FALSE); }
 
 {CT_LOOP}{WS}				{	yybegin(TAG);
 								print(yytext());
 								return token(Parser.CT_LOOP); }
 							
 							 
 {CT_SETTEXT}{WS}			{	yybegin(TAG);								
								print(yytext());
								return token(Parser.CT_SETTEXT); } 

}

/******* looking for nothing (end of nudroid) */
<END> .					{ error("There should be nothing", yytext()); }

/******* catch-all */
.{WS}					{ print("Caught something unfamiliar: ", yytext()); /* do nothing */ }

